﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;

using UniLua;

namespace SerialHelper
{
	public partial class frmMain : Form
	{
		private SerialPort serial = new SerialPort();
		private System.Timers.Timer time_timer = new System.Timers.Timer();
		private System.Timers.Timer loop_timer = new System.Timers.Timer();

		private int in_loop_timer = 0;

		private int tn_show_time = 0;
		private long count_send = 0, count_recv = 0;
		private string recv_file_path, send_file_path;

		private bool _recv_file = false;

		private bool _file_should_stop = false;
		private System.Threading.Thread file_thread = null;

		private bool _lua_should_stop = false;
		private System.Threading.Thread lua_thread = null;

		private AppConfig config = new AppConfig();

		private ILuaState lua;

		private Queue<byte> read_buffer = new Queue<byte>();

		#region 检测USB串口的热拔插
		// usb消息定义
		public const int WM_DEVICE_CHANGE = 0x219;
		public const int DBT_DEVICEARRIVAL = 0x8000;
		public const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004;
		public const UInt32 DBT_DEVTYP_PORT = 0x00000003;
		[StructLayout(LayoutKind.Sequential)]
		struct DEV_BROADCAST_HDR
		{
			public UInt32 dbch_size;
			public UInt32 dbch_devicetype;
			public UInt32 dbch_reserved;
		}

		[StructLayout(LayoutKind.Sequential)]
		protected struct DEV_BROADCAST_PORT_Fixed
		{
			public uint dbcp_size;
			public uint dbcp_devicetype;
			public uint dbcp_reserved;
			// Variable?length field dbcp_name is declared here in the C header file.
		}

		/// <summary>
		/// 检测USB串口的拔插
		/// </summary>
		/// <param name="m"></param>
		protected override void WndProc(ref Message m)
		{
			DEV_BROADCAST_HDR dbhdr;
			if(m.Msg == WM_DEVICE_CHANGE)        // 捕获USB设备的拔出消息WM_DEVICECHANGE
			{
				switch(m.WParam.ToInt32())
				{
				case DBT_DEVICE_REMOVE_COMPLETE:    // USB拔出  
					dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
					if(dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
					{
						string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
						Console.WriteLine("Port '" + portName + "' removed.");

						if(portName.CompareTo(serial.PortName) == 0)
						{
							serial_port_close();
						}
					}
					break;
				case DBT_DEVICEARRIVAL:             // USB插入获取对应串口名称
					dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
					if(dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
					{
						string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
						Console.WriteLine("Port '" + portName + "' arrived.");
					}
					break;
				}
			}
			base.WndProc(ref m);
		}
		#endregion

		public frmMain()
		{
			InitializeComponent();

			//////////////////////////////////////////////////////////////////////////////

			string[] checks = { "XOR异或校验", "ModBusLRC校验", "CRC16低字节在前", "CRC16高字节在前", "SUM16低字节在前", "SUM16高字节在前", "ModBusCRC低字节在前", "ModBusCRC高字节在前" };
			cbo_pack_check.Items.AddRange(checks);
			cbo_pack_check.Text = "XOR异或校验";

			string[] port_names = SerialPort.GetPortNames();
			Array.Sort(port_names, new CustomComparer());
			cbo_port_name.Items.AddRange(port_names);
			cbo_port_name.SelectedIndex = cbo_port_name.Items.Count > 0 ? 0 : -1;

			string[] baud_rates = { "110", "300", "600", "1200", "2400", "4800", "9600", "14400", "19200", "38400", "56000", "57600", "115200", "128000", "256000" };
			cbo_port_baud_rate.Items.AddRange(baud_rates);
			cbo_port_baud_rate.Text = "9600";

			string[] paritys = { "None", "Odd", "Even", "Mark", "Space" };
			cbo_port_parity.Items.AddRange(paritys);
			cbo_port_parity.Text = "None";

			string[] data_bits = { "5", "6", "7", "8" };
			cbo_port_data_bits.Items.AddRange(data_bits);
			cbo_port_data_bits.Text = "8";

			string[] stop_bits = { "1", "1.5", "2" };
			cbo_port_stop_bits.Items.AddRange(stop_bits);
			cbo_port_stop_bits.Text = "1";

			//////////////////////////////////////////////////////////////////////////////

			chk_recv_show.Checked = true;

			//////////////////////////////////////////////////////////////////////////////

			lua = LuaAPI.NewState();
			lua.L_OpenLibs();

			lua.RegisterGlobalFunc("read", lua_read);
			lua.RegisterGlobalFunc("write", lua_write);
			lua.RegisterGlobalFunc("send", lua_send);
			lua.RegisterGlobalFunc("sleep", lua_sleep);
			lua.RegisterGlobalFunc("show", lua_show);
			lua.RegisterGlobalFunc("help", lua_help);

			//////////////////////////////////////////////////////////////////////////////

			time_timer.Interval = 1;
			time_timer.Start();

			serial.DataReceived += serial_DataReceived;
			time_timer.Elapsed += time_timer_ElapsedEventHandler;
			loop_timer.Elapsed += loop_timer_ElapsedEventHandler;

			cbo_port_name.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
			cbo_port_baud_rate.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
			cbo_port_parity.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
			cbo_port_data_bits.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
			cbo_port_stop_bits.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
		}

		public class CustomComparer : System.Collections.IComparer
		{
			public int Compare(object x, object y)
			{
				string s1 = (string)x;
				string s2 = (string)y;

				if(s1.Length > s2.Length) return 1;
				if(s1.Length < s2.Length) return -1;
				return s1.CompareTo(s2);
			}
		}

		void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
		{
			int bytes = serial.BytesToRead;
			int i;

			try
			{
				if(bytes > 0)
				{
					byte[] buffer = new byte[bytes];//声明一个临时数组存储当前来的串口数据
					serial.Read(buffer, 0, bytes);//读取缓冲数据

					for(i = 0; i < bytes; i++)
					{
						if(read_buffer.Count() >= 65536)
						{
							read_buffer.Dequeue();
						}
						read_buffer.Enqueue(buffer[i]);
					}

					this.BeginInvoke((EventHandler)(delegate
					{
						count_recv += buffer.Length;
						slab_recv.Text = "接收:" + count_recv.ToString();

						if(chk_recv_show.Checked)
						{
							serial_recv_data(buffer);
						}
					}));
				}
			}
			catch(Exception ex)
			{
				this.BeginInvoke((EventHandler)(delegate
				{
					serial_port_close();

					MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				}));
			}
		}

		void time_timer_ElapsedEventHandler(object sender, EventArgs e)
		{
			if(tn_show_time > 0)
			{
				tn_show_time--;
			}
		}

		void loop_timer_ElapsedEventHandler(object sender, EventArgs e)
		{
			if(System.Threading.Interlocked.Exchange(ref in_loop_timer, 1) == 0)
			{
				serial_send_start();

				System.Threading.Interlocked.Exchange(ref in_loop_timer, 0);
			}
		}

		private void serial_port_open()
		{
			try
			{
				if(serial.IsOpen)
				{
					serial.Close();
				}

				serial.PortName = cbo_port_name.Text;
				serial.BaudRate = int.Parse(cbo_port_baud_rate.Text);
				serial.Parity = (Parity)Enum.Parse(typeof(Parity), cbo_port_parity.Text);
				serial.DataBits = int.Parse(cbo_port_data_bits.Text);

				StopBits[] stop_bits = { StopBits.One, StopBits.OnePointFive, StopBits.Two };
				serial.StopBits = stop_bits[cbo_port_stop_bits.SelectedIndex];

				serial.Open();
			}
			catch(Exception ex)
			{
				MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
			}

			if(serial.IsOpen)
			{
				btn_port_open.Image = Properties.Resources.open;
				btn_port_open.Text = "     关闭串口";
			}
			else
			{
				btn_port_open.Image = Properties.Resources.close;
				btn_port_open.Text = "     打开串口";
			}
		}

		private void serial_port_close()
		{
			try
			{
				if(loop_timer.Enabled)
				{
					send_loop_stop();
				}
				if(serial.IsOpen)
				{
					serial.Close();
				}
			}
			catch
			{

			}

			btn_port_open.Image = Properties.Resources.close;
			btn_port_open.Text = "     打开串口";
		}

		private void btn_port_open_Click(object sender, EventArgs e)
		{
			if(serial.IsOpen)
			{
				serial_port_close();
			}
			else
			{
				serial_port_open();
			}
		}

		private void cbo_port_name_DropDown(object sender, EventArgs e)
		{
			string port_name_old = cbo_port_name.Text;

			string[] port_names = SerialPort.GetPortNames();
			Array.Sort(port_names, new CustomComparer());
			cbo_port_name.Items.Clear();
			cbo_port_name.Items.AddRange(port_names);

			cbo_port_name.Text = port_name_old;

			if((cbo_port_name.Items.Count > 0) && (cbo_port_name.SelectedIndex < 0))
			{
				cbo_port_name.SelectedIndex = 0;
			}
		}

		private void cbo_port_SelectionChangeCommitted(object sender, EventArgs e)
		{
			if(serial.IsOpen)
			{
				serial_port_open();
			}
		}

		private void txt_show_send_KeyDown(object sender, KeyEventArgs e)
		{
			if((e.KeyCode == Keys.Enter) && (e.Modifiers == Keys.Control))
			{
				serial_send_start();
			}
		}

		private void txt_show_recv_KeyDown(object sender, KeyEventArgs e)
		{
			if(serial.IsOpen)
			{
				if(e.KeyCode == Keys.Up)
				{
					byte[] bytes = { 0x1B, 0x5B, 0x41 };
					serial_send_data(bytes);
					e.Handled = true;
				}
				else if(e.KeyCode == Keys.Down)
				{
					byte[] bytes = { 0x1B, 0x5B, 0x42 };
					serial_send_data(bytes);
					e.Handled = true;
				}
				else if(e.KeyCode == Keys.Right)
				{
					byte[] bytes = { 0x1B, 0x5B, 0x43 };
					serial_send_data(bytes);
					e.Handled = true;
				}
				else if(e.KeyCode == Keys.Left)
				{
					byte[] bytes = { 0x1B, 0x5B, 0x44 };
					serial_send_data(bytes);
					e.Handled = true;
				}
				else if(e.KeyCode == Keys.Escape)
				{
					byte[] bytes = { 0x1B };
					serial_send_data(bytes);
					e.Handled = true;
				}
				else if(e.KeyCode == Keys.Delete)
				{
					byte[] bytes = { 0x7F };
					serial_send_data(bytes);
					e.Handled = true;
				}
			}
		}

		private void txt_show_recv_KeyPress(object sender, KeyPressEventArgs e)
		{
			if(serial.IsOpen)
			{
				string str_tmp = e.KeyChar.ToString();
				byte[] bytes = Encoding.Default.GetBytes(str_tmp);
				serial_send_data(bytes);
			}

			e.Handled = true;
		}

		private void txt_show_send_clear()
		{
			if(chk_send_file.Checked)
			{
				chk_send_file.Checked = false;
			}
			else
			{
				txt_show_send.Clear();
			}
		}

		private void lbl_send_clear_Click(object sender, EventArgs e)
		{
			txt_show_send_clear();
		}

		private void txt_show_recv_clear()
		{
			if(chk_recv_file.Checked)
			{
				chk_recv_file.Checked = false;
			}
			else
			{
				txt_show_recv.Clear();
			}

			count_recv = 0;
			slab_recv.Text = "接收:" + count_recv.ToString();
			count_send = 0;
			slab_send.Text = "发送:" + count_send.ToString();
		}

		private void lbl_recv_clear_Click(object sender, EventArgs e)
		{
			txt_show_recv_clear();
		}

		private void chk_send_hex_CheckedChanged(object sender, EventArgs e)
		{
			try
			{
				if(!chk_send_file.Checked)
				{
					if(!chk_send_lua.Checked)
					{
						if(chk_send_hex.Checked)
						{
							txt_show_send.Text = byte_to_hex(Encoding.Default.GetBytes(txt_show_send.Text));
						}
						else
						{
							txt_show_send.Text = Encoding.Default.GetString(hex_to_byte(txt_show_send.Text));
						}
					}
				}
			}
			catch(Exception ee)
			{
				MessageBoxEx.Show(this, ee.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
		}

		private byte[] calc_modbus_crc(byte[] buffer)
		{
			UInt16 crc = 0xFFFF;
			int i, j;

			for(i = 0; i < buffer.Length; i++)
			{
				crc ^= buffer[i];
				for(j = 0; j < 8; j++)
				{
					if((crc & 0x0001) != 0)
					{
						crc >>= 1;
						crc ^= 0xA001;
					}
					else
					{
						crc >>= 1;
					}
				}
			}

			return BitConverter.GetBytes(crc);
		}

		private byte[] calc_crc_16(UInt16 polynomials, byte[] buffer)
		{
			UInt16 crc = 0x0000;
			int i, j;

			for(i = 0; i < buffer.Length; i++)
			{
				crc ^= (UInt16)((UInt16)buffer[i] << 8);
				for(j = 0; j < 8; j++)
				{
					if((crc & 0x8000) != 0)
					{
						crc <<= 1;
						crc ^= polynomials;
					}
					else
					{
						crc <<= 1;
					}
				}
			}

			return BitConverter.GetBytes(crc);
		}

		private byte[] calc_sum_16(byte[] buffer)
		{
			UInt16 sum = 0;
			int i;
			for(i = 0; i < buffer.Length; i++)
			{
				sum += buffer[i];
			}

			return BitConverter.GetBytes(sum);
		}

		private byte[] calc_xor(byte[] buffer)
		{
			byte xor = 0;
			int i;
			for(i = 0; i < buffer.Length; i++)
			{
				xor ^= buffer[i];
			}

			return new byte[] { xor };
		}

		private byte[] hex_to_byte(string str_hex)
		{
			List<byte> buffer = new List<byte>();
			int len;

			str_hex = str_hex.Trim();
			while(str_hex.Length > 0)
			{
				len = str_hex.Length;
				if(len > 2) len = 2;
				buffer.Add(byte.Parse(str_hex.Substring(0, len), System.Globalization.NumberStyles.HexNumber));
				str_hex = str_hex.Substring(len).TrimStart();
			}

			return buffer.ToArray();
		}

		private string byte_to_hex(byte[] buffer)
		{
			int i;
			string str_tmp = "";

			for(i = 0; i < buffer.Length; i++)
			{
				str_tmp += buffer[i].ToString("X2") + " ";
			}

			return str_tmp;
		}

		private void serial_recv_data(byte[] buffer)
		{
			string str_tmp = "";

			if(chk_recv_time.Checked)
			{
				if(tn_show_time == 0)
				{
					if(txt_show_recv.Text.Length > 0)
					{
						str_tmp += "\r\n";
					}
					str_tmp += "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "】";
				}

				tn_show_time = 10;
			}
			if(chk_recv_hex.Checked)
			{
				str_tmp += byte_to_hex(buffer);
			}
			else
			{
				str_tmp += Encoding.Default.GetString(buffer);
			}

			if(_recv_file)
			{
				File.AppendAllText(recv_file_path, str_tmp);
			}
			else
			{
				string[] strs = str_tmp.Split('\b');
				int i;

				for(i = 0; i < strs.Count(); i++)
				{
					if((txt_show_recv.TextLength >= 1) && (i > 0))
					{
						string str_select = "";
						txt_show_recv.Select(txt_show_recv.TextLength - 1, 1);
						if(txt_show_recv.SelectedText[0] == '\n')
						{
							if((txt_show_recv.TextLength >= 2) && (txt_show_recv.Text[txt_show_recv.TextLength - 2] == '\r'))
							{
								txt_show_recv.Select(txt_show_recv.TextLength - 2, 2);
							}
						}
						else
						{
							byte[] byte_tmp = Encoding.Default.GetBytes(txt_show_recv.SelectedText);
							if(byte_tmp.Length > 1)
							{
								byte[] byte_select = new byte[byte_tmp.Length - 1];
								Array.Copy(byte_tmp, byte_select, byte_select.Length);
								str_select = Encoding.Default.GetString(byte_select);
							}
						}
						txt_show_recv.SelectedText = str_select;
					}

					if(strs[i].LastIndexOf("\x1B[2K") >= 0)
					{
						strs[i] = strs[i].Substring(strs[i].LastIndexOf("\x1B[2K") + "\x1B[2K".Length);

						int pos = txt_show_recv.Text.LastIndexOf("\r\n");
						if(pos >= 0)
						{
							pos += "\r\n".Length;
							txt_show_recv.Select(pos, txt_show_recv.Text.Length - pos);
							txt_show_recv.SelectedText = "";
						}
						else
						{
							txt_show_recv.Text = "";
						}
					}

					txt_show_recv.AppendText(strs[i]);
				}
			}
		}

		private void serial_send_data(byte[] buffer)
		{
			if(buffer.Length > 0)
			{
				serial.Write(buffer, 0, buffer.Length);

				this.BeginInvoke((EventHandler)(delegate
				{
					count_send += buffer.Length;
					slab_send.Text = "发送:" + count_send.ToString();

					if(chk_send_show.Checked)
					{
						serial_recv_data(buffer);
					}
				}));
			}
		}

		private void send_file_thread()
		{
			FileStream file_reader = new FileStream(send_file_path, FileMode.Open, FileAccess.Read);
			long length = file_reader.Length;
			long offset = 0, count = 0;

			try
			{
				while((!_file_should_stop) && (offset < length))
				{
					count = length - offset;
					if(count > 32)
					{
						count = 32;
					}

					byte[] buffer = new byte[count];
					file_reader.Read(buffer, 0, (int)count);

					if(chk_send_hex.Checked)
					{
						buffer = hex_to_byte(Encoding.Default.GetString(buffer));
					}

					serial_send_data(buffer);

					offset += count;
				}
			}
			catch(Exception ex)
			{
				this.BeginInvoke((EventHandler)(delegate
				{
					serial_send_stop();

					MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				}));
			}
			finally
			{
				file_reader.Close();

				file_thread = null;

				this.BeginInvoke((EventHandler)(delegate
				{
					if(!chk_pack_loop.Checked)
					{
						btn_show_send.Text = "发送";
					}
				}));
			}
		}

		private void serial_send_text(string text)
		{
			byte[] buffer;

			try
			{
				if(chk_pack_head.Checked)
				{
					if(chk_send_hex.Checked)
					{
						serial_send_data(hex_to_byte(txt_pack_head.Text));
					}
					else
					{
						serial_send_data(Encoding.Default.GetBytes(txt_pack_head.Text));
					}
				}

				if(chk_send_hex.Checked)
				{
					buffer = hex_to_byte(text);
				}
				else
				{
					buffer = Encoding.Default.GetBytes(text);
				}
				serial_send_data(buffer);

				if(chk_pack_check.Checked)
				{
					byte[] check_buffer;
					byte byte_tmp;
					int check_type = 0;
					this.Invoke((EventHandler)(delegate
					{
						check_type = cbo_pack_check.SelectedIndex;
					}));
					switch(check_type)
					{
					case 0://XOR异或校验 
						check_buffer = calc_xor(buffer);
						serial_send_data(check_buffer);
						break;
					case 1://ModBusLRC校验
						check_buffer = calc_sum_16(buffer);
						serial_send_data(new byte[] { (byte)(-check_buffer[0]) });
						break;
					case 2://CRC16低字节在前
						check_buffer = calc_crc_16(0x1021, buffer);
						serial_send_data(check_buffer);
						break;
					case 3://CRC16高字节在前
						check_buffer = calc_crc_16(0x1021, buffer);
						byte_tmp = check_buffer[0];
						check_buffer[0] = check_buffer[1];
						check_buffer[1] = byte_tmp;
						serial_send_data(check_buffer);
						break;
					case 4://SUM16校验和低字节在前
						check_buffer = calc_sum_16(buffer);
						serial_send_data(check_buffer);
						break;
					case 5://SUM16校验和高字节在前
						check_buffer = calc_sum_16(buffer);
						byte_tmp = check_buffer[0];
						check_buffer[0] = check_buffer[1];
						check_buffer[1] = byte_tmp;
						serial_send_data(check_buffer);
						break;
					case 6://ModBusCRC低字节在前
						check_buffer = calc_modbus_crc(buffer);
						serial_send_data(check_buffer);
						break;
					case 7://ModBusCRC高字节在前
						check_buffer = calc_modbus_crc(buffer);
						byte_tmp = check_buffer[0];
						check_buffer[0] = check_buffer[1];
						check_buffer[1] = byte_tmp;
						serial_send_data(check_buffer);
						break;
					}
				}

				if(chk_pack_end.Checked)
				{
					if(chk_send_hex.Checked)
					{
						serial_send_data(hex_to_byte(txt_pack_end.Text));
					}
					else
					{
						serial_send_data(Encoding.Default.GetBytes(txt_pack_end.Text));
					}
				}
			}
			catch(Exception ex)
			{
				this.BeginInvoke((EventHandler)(delegate
				{
					serial_send_stop();

					MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				}));
			}
		}

		private int lua_read(ILuaState state)
		{
			if(read_buffer.Count() > 0)
			{
				state.PushBoolean(true);
				state.PushInteger(read_buffer.Dequeue());
			}
			else
			{
				state.PushBoolean(false);
				state.PushInteger(0);
			}

			return 2;
		}

		private int lua_write(ILuaState state)
		{
			int n_tmp = state.L_CheckInteger(1);
			byte[] buffer = { (byte)n_tmp };

			try
			{
				serial_send_data(buffer);
			}
			catch(Exception ex)
			{
				this.BeginInvoke((EventHandler)(delegate
				{
					serial_send_stop();

					MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				}));
			}

			return 0;
		}

		private int lua_send(ILuaState state)
		{
			string text = state.L_CheckString(1);

			serial_send_text(text);

			return 0;
		}

		private int lua_sleep(ILuaState state)
		{
			int timeout = state.L_CheckInteger(1);

			System.Threading.Thread.Sleep(timeout);

			return 0;
		}

		private int lua_show(ILuaState state)
		{
			string text = state.L_CheckString(1);

			this.Invoke((EventHandler)(delegate
			{
				txt_show_recv.AppendText(text);
			}));

			return 0;
		}

		private int lua_help(ILuaState state)
		{
			this.Invoke((EventHandler)(delegate
			{
				txt_show_recv.AppendText("///////////////////////////////////////////////" + "\r\n");
				txt_show_recv.AppendText("valid,value read(void);" + "\r\n");
				txt_show_recv.AppendText("void write(int value);" + "\r\n");
				txt_show_recv.AppendText("void send(string text);" + "\r\n");
				txt_show_recv.AppendText("void sleep(int timeout);" + "\r\n");
				txt_show_recv.AppendText("void show(string text);" + "\r\n");
				txt_show_recv.AppendText("void help();" + "\r\n");
				txt_show_recv.AppendText("///////////////////////////////////////////////" + "\r\n");
			}));

			return 0;
		}

		private void send_lua_thread()
		{
			try
			{
				var status = lua.L_DoString(txt_show_send.Text);
				if(status != ThreadStatus.LUA_OK)
				{
					throw new Exception(lua.ToString(-1));
				}
			}
			catch(Exception ex)
			{
				if(!_lua_should_stop)
				{
					this.BeginInvoke((EventHandler)(delegate
					{
						serial_send_stop();

						MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}));
				}
			}
			finally
			{
				lua_thread = null;

				if(!_lua_should_stop)
				{
					this.BeginInvoke((EventHandler)(delegate
					{
						if(!chk_pack_loop.Checked)
						{
							btn_show_send.Text = "发送";
						}
					}));
				}
			}
		}

		private void serial_send_start()
		{
			try
			{
				if(chk_send_lua.Checked)
				{
					if(lua_thread == null)
					{
						lua_thread = new System.Threading.Thread(send_lua_thread);
						_lua_should_stop = false;
						lua_thread.Start();

						if(!chk_pack_loop.Checked)
						{
							btn_show_send.Text = "停止";
						}
					}
				}
				else if(chk_send_file.Checked)
				{
					if(file_thread == null)
					{
						file_thread = new System.Threading.Thread(send_file_thread);
						_file_should_stop = false;
						file_thread.Start();

						if(!chk_pack_loop.Checked)
						{
							btn_show_send.Text = "停止";
						}
					}
				}
				else
				{
					serial_send_text(txt_show_send.Text);

					if(chk_send_clear.Checked)
					{
						txt_show_send.Clear();
					}
				}
			}
			catch(Exception ex)
			{
				serial_send_stop();

				MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}
		}

		private void send_loop_start()
		{
			loop_timer.Enabled = true;
			btn_show_send.Text = "停止";
		}

		private void send_loop_stop()
		{
			if(lua_thread != null)
			{
				_lua_should_stop = true;
				lua_thread.Abort();
				while(lua_thread != null)
				{
					Application.DoEvents();
				}
				btn_show_send.Text = "发送";
			}
			else if(file_thread != null)
			{
				while(file_thread != null)
				{
					_file_should_stop = true;
					Application.DoEvents();
				}
			}

			loop_timer.Enabled = false;
			btn_show_send.Text = "发送";
		}

		private bool serial_send_stop()
		{
			if(loop_timer.Enabled)
			{
				send_loop_stop();

				return true;
			}
			else if(lua_thread != null)
			{
				_lua_should_stop = true;
				lua_thread.Abort();
				while(lua_thread != null)
				{
					Application.DoEvents();
				}
				btn_show_send.Text = "发送";

				return true;
			}
			else if(file_thread != null)
			{
				while(file_thread != null)
				{
					_file_should_stop = true;
					Application.DoEvents();
				}

				return true;
			}

			read_buffer.Clear();

			return false;
		}

		private void btn_show_send_Click(object sender, EventArgs e)
		{
			if(!serial_send_stop())
			{
				if(chk_pack_loop.Checked)
				{
					send_loop_start();
				}

				serial_send_start();
			}
		}

		private void chk_pack_loop_CheckedChanged(object sender, EventArgs e)
		{
			if(chk_pack_loop.Checked)
			{
				num_pack_loop.Enabled = false;
				loop_timer.Interval = (int)num_pack_loop.Value;
			}
			else
			{
				send_loop_stop();
				num_pack_loop.Enabled = true;
			}
		}

		private void chk_recv_file_CheckedChanged(object sender, EventArgs e)
		{
			if(chk_recv_file.Checked)
			{
				SaveFileDialog fileDialog = new SaveFileDialog();

				fileDialog.Title = "接收转向文件...";
				fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
				if(fileDialog.ShowDialog() == DialogResult.OK)
				{
					recv_file_path = fileDialog.FileName;
				}
				else
				{
					chk_recv_file.Checked = false;
				}
			}

			if(chk_recv_file.Checked)
			{
				txt_show_recv.Text = "文件路径: \"" + recv_file_path + "\"";
				txt_show_recv.BackColor = System.Drawing.SystemColors.Control;
			}
			else
			{
				txt_show_recv.Clear();
				txt_show_recv.BackColor = System.Drawing.SystemColors.Window;
			}

			_recv_file = chk_recv_file.Checked;
		}

		private void chk_send_file_CheckedChanged(object sender, EventArgs e)
		{
			if(chk_send_file.Checked)
			{
				OpenFileDialog fileDialog = new OpenFileDialog();

				fileDialog.Title = "发送文件数据...";
				fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
				if(fileDialog.ShowDialog() == DialogResult.OK)
				{
					send_file_path = fileDialog.FileName;
				}
				else
				{
					chk_send_file.Checked = false;
				}
			}

			if(chk_send_file.Checked)
			{
				txt_show_send.Text = "文件路径: \"" + send_file_path + "\"\r\n" + "文件长度: " + new FileInfo(send_file_path).Length.ToString() + "字节";
				txt_show_send.ReadOnly = true;

				chk_send_lua.Enabled = false;
				chk_send_lua.Checked = false;
			}
			else
			{
				chk_send_lua.Enabled = true;

				txt_show_send.Clear();
				txt_show_send.ReadOnly = false;

				if(file_thread != null)
				{
					while(file_thread != null)
					{
						_file_should_stop = true;
						Application.DoEvents();
					}
				}
			}
		}

		private void slab_info_Click(object sender, EventArgs e)
		{
			System.Diagnostics.Process.Start("http://www.mysoow.com"); //在浏览器中打开链接
		}

		private void lbl_recv_save_Click(object sender, EventArgs e)
		{
			SaveFileDialog fileDialog = new SaveFileDialog();

			fileDialog.Title = "保存数据";
			fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
			if(fileDialog.ShowDialog() == DialogResult.OK)
			{
				File.WriteAllText(fileDialog.FileName, txt_show_recv.Text);
			}
		}

		private void lbl_send_load_Click(object sender, EventArgs e)
		{
			OpenFileDialog fileDialog = new OpenFileDialog();

			fileDialog.Title = "载入数据";
			fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
			if(fileDialog.ShowDialog() == DialogResult.OK)
			{
				chk_send_file.Checked = false;
				txt_show_send.Text = File.ReadAllText(fileDialog.FileName);
			}
		}

		private void config_load()
		{
			config.Reload();

			cbo_port_name.Text = config.cbo_port_name;
			cbo_port_baud_rate.Text = config.cbo_port_baud_rate;
			cbo_port_parity.Text = config.cbo_port_parity;
			cbo_port_data_bits.Text = config.cbo_port_data_bits;
			cbo_port_stop_bits.Text = config.cbo_port_stop_bits;

			chk_recv_time.Checked = config.chk_recv_time;
			chk_recv_hex.Checked = config.chk_recv_hex;
			chk_send_show.Checked = config.chk_send_show;
			chk_recv_show.Checked = config.chk_recv_show;

			chk_send_clear.Checked = config.chk_send_clear;
			chk_send_hex.Checked = config.chk_send_hex;
			chk_send_lua.Checked = config.chk_send_lua;

			chk_pack_head.Checked = config.chk_pack_head;
			txt_pack_head.Text = config.txt_pack_head;
			chk_pack_check.Checked = config.chk_pack_check;
			cbo_pack_check.Text = config.cbo_pack_check;
			chk_pack_end.Checked = config.chk_pack_end;
			txt_pack_end.Text = config.txt_pack_end;
			num_pack_loop.Value = config.num_pack_loop;
			chk_pack_loop.Checked = config.chk_pack_loop;

			spc_back.Panel1Collapsed = config.chk_show_left;
			spc_show.Panel2Collapsed = config.chk_show_down;

			txt_show_send.Text = config.txt_show_send;
		}

		private void config_save()
		{
			config.cbo_port_name = cbo_port_name.Text;
			config.cbo_port_baud_rate = cbo_port_baud_rate.Text;
			config.cbo_port_parity = cbo_port_parity.Text;
			config.cbo_port_data_bits = cbo_port_data_bits.Text;
			config.cbo_port_stop_bits = cbo_port_stop_bits.Text;

			config.chk_recv_time = chk_recv_time.Checked;
			config.chk_recv_hex = chk_recv_hex.Checked;
			config.chk_send_show = chk_send_show.Checked;
			config.chk_recv_show = chk_recv_show.Checked;

			config.chk_send_clear = chk_send_clear.Checked;
			config.chk_send_hex = chk_send_hex.Checked;
			config.chk_send_lua = chk_send_lua.Checked;

			config.chk_pack_head = chk_pack_head.Checked;
			config.txt_pack_head = txt_pack_head.Text;
			config.chk_pack_check = chk_pack_check.Checked;
			config.cbo_pack_check = cbo_pack_check.Text;
			config.chk_pack_end = chk_pack_end.Checked;
			config.txt_pack_end = txt_pack_end.Text;
			config.chk_pack_loop = chk_pack_loop.Checked;
			config.num_pack_loop = num_pack_loop.Value;

			config.chk_show_left = spc_back.Panel1Collapsed;
			config.chk_show_down = spc_show.Panel2Collapsed;

			config.txt_show_send = txt_show_send.Text;

			config.Save();
		}

		private void frmMain_Load(object sender, EventArgs e)
		{
			config_load();
		}

		private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
		{
			serial_send_stop();
			config_save();
		}

		private void CopyToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if(txt_show_recv.SelectionLength > 0)
			{
				Clipboard.SetText(txt_show_recv.SelectedText);
			}
		}

		private void PasteToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if(serial.IsOpen)
			{
				serial_send_data(Encoding.Default.GetBytes(Clipboard.GetText()));
			}
		}

		private void SelectAllToolStripMenuItem_Click(object sender, EventArgs e)
		{
			txt_show_recv.SelectAll();
		}

		private void ClearToolStripMenuItem_Click(object sender, EventArgs e)
		{
			txt_show_recv_clear();
		}

		private void menu_show_recv_Opened(object sender, EventArgs e)
		{
			CopyToolStripMenuItem.Enabled = txt_show_recv.SelectionLength > 0;
			PasteToolStripMenuItem.Enabled = Clipboard.ContainsText();
			SelectAllToolStripMenuItem.Enabled = txt_show_recv.SelectionLength < txt_show_recv.Text.Length;
			ClearToolStripMenuItem.Enabled = txt_show_recv.TextLength > 0;
		}

		private void lbl_hide_left_Click(object sender, EventArgs e)
		{
			spc_back.Panel1Collapsed = !spc_back.Panel1Collapsed;
		}

		private void lbl_hide_left_MouseEnter(object sender, EventArgs e)
		{
			lbl_hide_left.Image = spc_back.Panel1Collapsed ? Properties.Resources.right : Properties.Resources.left;
		}

		private void lbl_hide_left_MouseLeave(object sender, EventArgs e)
		{
			lbl_hide_left.Image = null;
		}

		private void lbl_hide_down_Click(object sender, EventArgs e)
		{
			spc_show.Panel2Collapsed = !spc_show.Panel2Collapsed;
		}

		private void lbl_hide_down_MouseEnter(object sender, EventArgs e)
		{
			lbl_hide_down.Image = spc_show.Panel2Collapsed ? Properties.Resources.up : Properties.Resources.down;
		}

		private void lbl_hide_down_MouseLeave(object sender, EventArgs e)
		{
			lbl_hide_down.Image = null;
		}
	}
}
